package com.pig4cloud.pigx.robot.depth;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.pig4cloud.pigx.contract.enums.ContractOrderEnum;
import com.pig4cloud.pigx.robot.entity.RobotConfig;
import com.pig4cloud.pigx.robot.mapper.RobotConfigMapper;
import com.pig4cloud.pigx.robot.rule.PriceRule;
import com.pig4cloud.pigx.robot.service.RobotConfigService;
import com.pig4cloud.pigx.robot.vo.RobotConfigVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.core.annotation.Order;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

@Slf4j
@Order(1)
@Component
public class GenerateDepth implements ApplicationRunner {
	@Autowired
	private RobotConfigService robotConfigService;
	private List<RobotConfigVO> robotConfigList;
	@Autowired
	private RobotConfigMapper configMapper;
	@Autowired
	private RedisTemplate redis;
	@Override
	public void run(ApplicationArguments args ) throws Exception {
		log.info("auto place order init...");
		init();
	}
	private void init(){
		robotConfigList = configMapper.initRobotList();
		 generate();
	}
	private void generate(){
		ScheduledExecutorService service= Executors.newSingleThreadScheduledExecutor();
		service.scheduleAtFixedRate(() -> {
			for(int i=0 ; i<robotConfigList.size();i++) {
				try {
					RobotConfigVO item = robotConfigList.get(i);
					String symbol = item.getSymbolName().toUpperCase();
					//if(!item.getSymbolName().equals("BTCUSDT")) continue;
					String redis_key  = symbol+"_last_price";
					Object price  = redis.opsForValue().get(redis_key);
					if(price == null ){
						log.error("robot auto depth   error ,last price is null ");
						continue;
					}
					BigDecimal lastprice =new  BigDecimal(price.toString());
					if(lastprice.compareTo(BigDecimal.ZERO)==0){
						log.error("robot auto depth   error ,last price is 0 ");
						continue;
					}
//					List<String> levels = getLevelFromRedis(symbol);
					// 深度图选项计算方式更新
					// 获取币对外币精度
					Integer accuracy = item.getMainCurAccuracy();
					List<String> levels = calcLevel(accuracy);
					saveLevelToRedis(item.getSymbolName().toUpperCase(),levels);
					levels.forEach(level->{
						getDepthSide(symbol,lastprice,level,ContractOrderEnum.Side.MORE.getValue());
						getDepthSide(symbol,lastprice,level,ContractOrderEnum.Side.EMPTY.getValue());
					});


				} catch (Exception ex) {
					log.error("[generate] depth error:{0}",ex.getMessage());
				}
			}

		}, 5000, 100, TimeUnit.MILLISECONDS);
	}

	private TreeMap<BigDecimal,List<BigDecimal>> getDepthSide(String symbol ,BigDecimal lastprice,String level,String side){
		try{

			TreeMap<BigDecimal,List<BigDecimal>> depMap = new TreeMap<BigDecimal,List<BigDecimal>>();
			depMap = getDepthFromRedis(symbol,side,level);
			depMap = limit(depMap,lastprice,side);

			Random random = new Random();

//			BigDecimal num =new BigDecimal(random.nextInt(decimal.intValue())+1);


			depMap= fill(depMap,lastprice,level,side);
			saveDepthToRedis(depMap,symbol,side,level);
			return depMap;
		}catch (Exception ex){
			throw  ex;
		}

	}
	private final String[] DL = {"0.1","0.01","0.001","0.0001","0.00001","0.000001","0.0000001","0.00000001","0.000000001","0.0000000001","0.00000000001"};
	private final String[] DL_INT = {"1","10","100"};
	private List<String> calcLevel(Integer accuracy){
		List<String> level  = new ArrayList<>(4);
		BigDecimal min = BigDecimal.ONE;
		for (int i = 0; i < accuracy; i++) {
			min = min.divide(BigDecimal.TEN,accuracy,BigDecimal.ROUND_HALF_UP);
		}
		// 设置初始精度
		level.add(min.stripTrailingZeros().toPlainString());
		// 精度选项列最多为4个选项
		for (int i = 0; i < 3; i++) {
			min = min.multiply(BigDecimal.TEN);
			level.add(min.stripTrailingZeros().toPlainString());
		}
		return level;
	}
	private List<String> getLevelFromRedis(String symbol){
		try{
			String key = "depth:"+symbol+":levellist";

			return  redis.opsForList().range(key,0,-1);

		}catch (Exception ex){
			throw  ex;
		}
	}
	private void saveLevelToRedis(String symbol,List<String> levels){
		String key = "depth:"+symbol+":levellist";
		try{
			redis.delete(key);

			redis.opsForList().leftPushAll(key,levels);
		}catch (Exception ex){
			throw ex;
		}
	}
	private BigDecimal calcDepthPrice(String level,BigDecimal orderPrice){
		BigDecimal depthPrice = BigDecimal.ZERO;
		try{
			int level_scale = new BigDecimal(level).stripTrailingZeros().scale();

			if(level_scale > 0 ){
				depthPrice =orderPrice.setScale(level_scale, RoundingMode.DOWN);
				if(depthPrice.compareTo(BigDecimal.ZERO) == 0){
					return depthPrice;
				}
			}else {
				Integer intPrice =orderPrice.intValue();
				String stringPrice = intPrice.toString();
				if ("1".equals(level) && intPrice > 0 ) {
					depthPrice = new BigDecimal(intPrice).setScale(0, RoundingMode.DOWN);
				}
				if ("10".equals(level) && intPrice > 9 ) {
					stringPrice = stringPrice.substring(0, stringPrice.length() - 1) + "0";
					depthPrice = new BigDecimal(stringPrice).setScale(0, RoundingMode.DOWN);
				}
				if ("100".equals(level)) {
					stringPrice = stringPrice.substring(0, stringPrice.length() - 2) + "00";
					depthPrice = new BigDecimal(stringPrice).setScale(0, RoundingMode.DOWN);
				}
			}



		}catch (Exception ex){
			log.error("calc depth price error"+ex.getMessage());
		}

		return depthPrice;
	}
	private   void saveDepthToRedis(TreeMap<BigDecimal,List<BigDecimal>> depMap, String symbol, String side, String level){



		redis.opsForValue().set("depth:"+symbol+":"+side+":"+level, JSONObject.toJSONString(depMap));

	}
	private TreeMap<BigDecimal,List<BigDecimal>> getDepthFromRedis(String symbol,String side,String level){
		TreeMap<BigDecimal,List<BigDecimal>>  depTreeMap = new TreeMap<BigDecimal,List<BigDecimal>>();

		try{
			Object o   = redis.opsForValue().get("depth:"+symbol+":"+side+":"+level);
			if(o != null){
				JSONObject jsonObject = JSONObject.parseObject(o.toString());
				Gson gson = new Gson();
				depTreeMap = gson.fromJson(jsonObject.toJSONString(), new TypeToken<TreeMap<BigDecimal, List<BigDecimal>>>() {
				}.getType());



			}
//			if(depMap != null){
//				depTreeMap.putAll(depMap);
//			}

		}catch (Exception ex){

		}

		return depTreeMap;

	}
	private TreeMap<BigDecimal,List<BigDecimal>> limit(TreeMap<BigDecimal,List<BigDecimal>> depthMap,BigDecimal price,String side){
		int limitCount = 20;

		//if(depthMap.size() <= limitCount ) return depthMap;
		//NavigableMap<BigDecimal,BigDecimal> nmap = depthMap.descendingMap();
		if(side.equals(ContractOrderEnum.Side.MORE.getValue())){

			Iterator<BigDecimal> iterator = depthMap.keySet().iterator();

			while(iterator.hasNext()){

				BigDecimal k = iterator.next();
				if(k.compareTo(price) > 0 ){
					iterator.remove();
					depthMap.remove(k);
					continue;
				}
				if(depthMap.size() > limitCount){
					iterator.remove();
					depthMap.remove(k);
				}

			}
		}else{


			NavigableMap<BigDecimal,List<BigDecimal>> nmap = depthMap.descendingMap();
			Iterator<BigDecimal> iterator = nmap.keySet().iterator();
			while(iterator.hasNext()){
				BigDecimal k = iterator.next();
				if(k.compareTo(price) < 0 ){
					iterator.remove();
					depthMap.remove(k);
					continue;
				}
				if(depthMap.size() > limitCount){
					iterator.remove();
					depthMap.remove(k);
				}
			}
		}

		return depthMap ;
	}
	private TreeMap<BigDecimal,List<BigDecimal>> fill(TreeMap<BigDecimal,List<BigDecimal>> depthMap,BigDecimal price,String level,String side){
		int limitCount = 20;
		//	if(depthMap.size() >=20 ) return  depthMap;
		//	depthMap = new TreeMap<BigDecimal,List<BigDecimal>>();
		try{
			BigDecimal depthPrice = calcDepthPrice(level,price);
			BigDecimal lastNum = BigDecimal.ZERO;
			BigDecimal num = BigDecimal.ZERO;
			if(side.equals(ContractOrderEnum.Side.MORE.getValue())){
				while(limitCount-- > 0){

					BigDecimal decimal = PriceRule.getNum(price);
					num = num.add(decimal);

					depthPrice = depthPrice.subtract(new BigDecimal(level)).setScale(new BigDecimal(level).scale());
					if(depthPrice.compareTo(BigDecimal.ZERO) <= 0){
						continue;
					}

					if(depthMap.get(depthPrice) != null){
						List<BigDecimal> list = depthMap.get(depthPrice);
						lastNum = lastNum.add(num);

						if(lastNum.compareTo(BigDecimal.ZERO) == 0){
							lastNum = num;
						}
						list.set(0,num);
						list.set(1,lastNum);

						depthMap.put(depthPrice,list);
					}else{
						List<BigDecimal> list = new ArrayList<BigDecimal>();
						if(lastNum.compareTo(BigDecimal.ZERO) == 0){
							lastNum = num;
						}
						lastNum = lastNum.add(num);
						list.add(0,num);
						list.add(1,lastNum);
						depthMap.put(depthPrice,list);

					}

				}
			}else{
				while(limitCount-- > 0){

					BigDecimal decimal = PriceRule.getNum(price);
					num = num.add(decimal);

					depthPrice = depthPrice.add(new BigDecimal(level)).setScale(new BigDecimal(level).scale());

					if(depthMap.get(depthPrice) != null){
						List<BigDecimal> list = depthMap.get(depthPrice);
						lastNum = lastNum.add(num);
						list.set(0,num);
						list.set(1,lastNum);
						depthMap.put(depthPrice,list);
					}else{
						List<BigDecimal> list = new ArrayList<BigDecimal>();
						lastNum = lastNum.add(num);
						list.add(0,num);
						list.add(1,lastNum);

						depthMap.put(depthPrice,list);

					}
				}
			}

		}catch (Exception ex){
			throw ex;
		}

		return depthMap;

	}
}
